#include "qmicrophone.h"
#include <QDebug>
#include <QTimer>

const int QMicrophone::DataFrequencyHzInput = 48000;

QMicrophone::QMicrophone(QObject *parent) :
    QObject(parent)
  ,audio(NULL)
  ,volumeManager(NULL)
  ,volumeAnalyzer(NULL)
  ,recording(false)
{
    format.setFrequency(QMicrophone::DataFrequencyHzInput);
    format.setChannels(1);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultInputDevice());

    if (!info.isFormatSupported(format)) {
        qWarning()<<"default format not supported try to use nearest";
        format = info.nearestFormat(format);

        if (!info.isFormatSupported(format)) {
            qWarning()<<"again format not supported try to use nearest";
        } else {
            qDebug() << QString::number(format.frequency());
            qDebug() << QString::number(format.channels());
            qDebug() << QString::number(format.sampleSize());
            qDebug() << QString::number(format.sampleRate());
            qDebug() << format.codec();
            qDebug() << QString::number(format.byteOrder());
            qDebug() << QString::number(format.sampleType());
        }
    }

    audio = new QAudioInput(info, format, this);
    audio->setBufferSize(8192);
    connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(microphoneStateChanged(QAudio::State)));

    volumeManager = new QAudioInput(info, format, this);
    volumeManager->setBufferSize(8192);
    connect(volumeManager, SIGNAL(stateChanged(QAudio::State)), this, SLOT(volumeManagerStateChanged(QAudio::State)));

    volumeAnalyzer = new QVolumeAnalyzer(format, this);
    connect(volumeAnalyzer,SIGNAL(thresholdExceeded(int)), this, SLOT(volumeThresholdExceeded(int)));
    connect(volumeAnalyzer,SIGNAL(clap()), this, SLOT(clapEvent()));

    QAudioDeviceInfo outputDeviceInfo(QAudioDeviceInfo::defaultOutputDevice());
    if (!outputDeviceInfo.isFormatSupported(format)) {
        format = outputDeviceInfo.nearestFormat(format);
    }
    m_audioOutput = new QAudioOutput(outputDeviceInfo, format, this);

}

void QMicrophone::startRecording(QString name, int time)
{
    outputFile.setFileName(name);
    outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate );

    if( time > 0 )
        QTimer::singleShot(time, this, SLOT(stop()));

    audio->start(&outputFile);
    recording = true;
    emit recordingStarted();
}

void QMicrophone::stopRecording()
{
    if( audio ) {
        audio->stop();
        outputFile.close();
        recording = false;
        //delete audio;
        emit recordingStopped();
        //play("C:/test.wav");
    }
}

void QMicrophone::startVolumeAnalysis(int time)
{
    if( time > 0 )
        QTimer::singleShot(time, this, SLOT(stopVolumeAnalysis()));

    volumeAnalyzer->start();
    volumeManager->start(volumeAnalyzer);

    emit volumeAnalysisStarted();
}

void QMicrophone::stopVolumeAnalysis()
{
    if( volumeManager ) {
        volumeManager->stop();
        volumeAnalyzer->stop();
        //delete volumeManager;
        emit volumeAnalysisStopped();
    } else
        qDebug() << "volumeManager null";
}

void QMicrophone::volumeThresholdExceeded(int volume){
    emit thresholdExceeded(volume);
}

void QMicrophone::clapEvent(){
    emit clap();
}

void QMicrophone::play(QString name) {
    inputFile.setFileName(name);
    inputFile.open( QIODevice::ReadOnly | QIODevice::Truncate );
    m_audioOutput->start(&inputFile);
}

QStringList QMicrophone::supportedCodecs()
{
    return QAudioDeviceInfo::defaultInputDevice().supportedCodecs();
}

QString QMicrophone::statusString() {
    QString result;

    switch(audio->state()) {
    case QAudio::ActiveState: result =  "Active state"; break;
    case QAudio::SuspendedState: result =  "Suspended state"; break;
    case QAudio::StoppedState: result =  "Stopped state"; break;
    case QAudio::IdleState: result =  "Idle state"; break;
    default : result =  "State not supported";
    }

    return result;
}

void QMicrophone::microphoneStateChanged(QAudio::State status)
{
    switch(status) {
    case QAudio::ActiveState: qDebug() << "Active state"; break;
    case QAudio::SuspendedState: qDebug() << "Suspended state"; break;
    case QAudio::StoppedState: qDebug() << "Stopped state"; break;
    case QAudio::IdleState: qDebug() << "Idle state"; break;
    default : qDebug() << "State not supported";
    }

    if (audio->error() != QAudio::NoError) {
        switch(audio->error()){
        case QAudio::OpenError : qDebug() << "An error opening the audio device"; break;
        case QAudio::IOError : qDebug() << "An error occurred during read/write of audio device"; break;
        case QAudio::UnderrunError : qDebug() << "Audio data is not being fed to the audio device at a fast enough rate"; break;
        case QAudio::FatalError: qDebug() << "A non-recoverable error has occurred, the audio device is not usable at this time."; break;
        default : qDebug() << "Generic error";
        }
    }
}

void QMicrophone::volumeManagerStateChanged(QAudio::State status)
{
    switch(status) {
    case QAudio::ActiveState: qDebug() << "Active state"; break;
    case QAudio::SuspendedState: qDebug() << "Suspended state"; break;
    case QAudio::StoppedState: qDebug() << "Stopped state"; break;
    case QAudio::IdleState: qDebug() << "Idle state"; break;
    default : qDebug() << "State not supported";
    }

    if (audio->error() != QAudio::NoError) {
        switch(audio->error()){
        case QAudio::OpenError : qDebug() << "An error opening the audio device"; break;
        case QAudio::IOError : qDebug() << "An error occurred during read/write of audio device"; break;
        case QAudio::UnderrunError : qDebug() << "Audio data is not being fed to the audio device at a fast enough rate"; break;
        case QAudio::FatalError: qDebug() << "A non-recoverable error has occurred, the audio device is not usable at this time."; break;
        default : qDebug() << "Generic error";
        }
    }
}

bool QMicrophone::isRecording() {
    return recording;
}

int  QMicrophone::threshold() {
    return ( volumeAnalyzer != NULL ) ?  volumeAnalyzer->threshold : -1;
}

void  QMicrophone::setThreshold(int value) {
    if ( volumeAnalyzer != NULL ) {
        volumeAnalyzer->threshold = value;
    }
}
